﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace DistributedGame.Service
{
	/// TODO:
	/// - Authentication / Authorization
	/// - Connection keep-alive [DONE - http://www.smartasses.be/2009/01/26/wcf-reliable-session-and-keep-alives/ ]
	/// - Client reconnect after fault (i.e., keeps game state and player slot until Server decides to close it)
	/// - Separate concepts of GameClient and GamePlayer (e.g., two players can play from the same remote client)
	/// - Handle exceptions that occur on asynchronous events
	/// - Game ideas:
	/// 	- Modulus N
	/// 	- Amazons
	/// 	- http://en.wikipedia.org/wiki/List_of_abstract_strategy_games

    [Serializable,DataContract]
    public class ClientConnectRequest
    {
        [DataMember]
        public string DesiredNickname { get; internal set; }
    }

    [Serializable,DataContract]
    public class ClientConnectionInfo
    {
		//use [NonSerialized] to hide temp fields

		public static readonly string HOST_ID = "{host}";
		public bool IsHost { get { return SessionId == HOST_ID; } }

        [DataMember]
        public string SessionId { get; internal set; }
		
        [DataMember]
        public string Nickname { get; set; } //TODO: players should be able to rename themselves
        
        /// Possible other properties:
		/// - Network Address (URI)
		/// - Client Version
		/// - DateTime connected
    }

	[Serializable,DataContract]
	[ImmutableObject(true)]
	[KnownType(typeof(ClientMessage))] //http://blogs.msdn.com/youssefm/archive/2009/04/21/understanding-known-types.aspx
	public class RemoteMessage
	{
	    [DataMember]
	    public string Content { get; internal set; }
        
	    [DataMember]
	    public DateTime Time { get; internal set; }

		public RemoteMessage(string content)
		{
			this.Time = DateTime.Now;
			this.Content = content;
		}
	}

	[Serializable,DataContract]
	[ImmutableObject(true)]
	public class ClientMessage : RemoteMessage
	{
	    [DataMember]
	    public ClientConnectionInfo Sender { get; internal set; }

		public ClientMessage(ClientConnectionInfo sender, string content) : base(content)
		{
			this.Sender = sender;
		}
	}
    
    [ServiceContract(CallbackContract = typeof(IGameClientCallback), SessionMode = SessionMode.Required)]
    public interface IGameHost
    {
		[OperationContract(IsInitiating = true)]
		ClientConnectionInfo Connect(ClientConnectRequest request);

		[OperationContract(IsOneWay = true)]
		void Say(string message);

		//[OperationContract(IsOneWay = true)]
		//void Whisper(string msg, ClientInfo receiver);

		//[OperationContract(IsOneWay = true)]
		//void IsWriting(ClientInfo client);

		[OperationContract(IsOneWay = true, IsTerminating = true)]
		void Disconnect();
    }

    public interface IGameClientCallback
    {
		[OperationContract(IsOneWay = true)]
		void Receive(RemoteMessage message);

		[OperationContract(IsOneWay = true)]
		void RefreshClients(IList<ClientConnectionInfo> clients);

		//[OperationContract(IsOneWay = true)]
		//void Receive(Message message);

		//[OperationContract(IsOneWay = true)]
		//void ClientIsTyping(ClientInfo client);

		//[OperationContract(IsOneWay = true)]
		//void ClientJoined(ClientInfo client);

		//[OperationContract(IsOneWay = true)]
		//void ClientLeft(ClientInfo client);

		[OperationContract(IsOneWay = true, IsTerminating = true)]
		//[OperationBehavior(ReleaseInstanceMode=ReleaseInstanceMode.AfterCall)]
		void ServerClosing();
    }
}
